using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;


namespace ArkanoidX
{
    /// <summary>
    /// This is a _game component that implements IUpdateable.
    /// </summary>
    public class Bricks : Microsoft.Xna.Framework.GameComponent
    {
        /*
         * the number of bricks for each line is fixed as 11
         */
        protected const int _kBricksPerLine = 11;
        protected const int _MaxLines = 11;

        protected Rectangle _screenBounds;

        protected Rectangle _currentBounds; // the envoloping rectangle of the bricks

        protected List<Brick> _bricks = new List<Brick>();

        protected Game _game;


        public Bricks(Game game, Rectangle screenBounds)
            : base(game)
        {
            _screenBounds = screenBounds;

            _game = game;

            // TODO: Construct any child components here
        }

        /// <summary>
        /// Allows the _game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        /// <summary>
        /// Allows the _game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here

            base.Update(gameTime);
        }

        public void LoadBricks()
        {
            float x = _screenBounds.Left;
            float y = _screenBounds.Top;
            float brickSpaceWidth = (float)Math.Round((double)_screenBounds.Width / _kBricksPerLine);
            float brickSpaceHeight = brickSpaceWidth * 0.3f;
            float brickWidth = brickSpaceWidth * 0.95f;
            float brickHeight = brickSpaceHeight * 0.90f;
            float brickHalfSpaceWidth = brickSpaceWidth * 0.5f;
            float brickHalfSpaceHeight = brickSpaceHeight * 0.5f;


            // remove all existing bricks
            for (int i = 0; i < _game.Components.Count; )
            {
                IGameComponent component = _game.Components[i];
                if (component is Brick)
                {
                    _game.Components.RemoveAt(i);
                }
                else
                {
                    i++;
                }
            }

            _bricks.Clear();

            Dictionary<char, Brick.State[]>brickMap = new Dictionary<char,Brick.State[]>();

            brickMap.Add('R', 
                new Brick.State[] {
                new Brick.NormalState(_game.Content.Load<Texture2D>("brickRed"), 
                    new Vector2(brickWidth, brickHeight))}
            );

            brickMap.Add('G',
                new Brick.State[] {
                new Brick.NormalState(_game.Content.Load<Texture2D>("brickGreen"),
                    new Vector2(brickWidth, brickHeight))}
            );

            brickMap.Add('B',
                new Brick.State[] {
                new Brick.NormalState(_game.Content.Load<Texture2D>("brickBlue"),
                    new Vector2(brickWidth, brickHeight))}
            );

            if (!File.Exists("layout.dat"))
            {
                using (StreamWriter sw = new StreamWriter("layout.dat"))
                {
                    sw.WriteLine();
                    sw.WriteLine();
                    sw.WriteLine("RGB GBR BRG");
                    sw.WriteLine("GBRGBRGBRGB");
                    sw.WriteLine("BRGBRGBRGBR");
                    sw.WriteLine("RGBRGBRGBRG");
                    sw.WriteLine("GBRGBRGBRGB");
                }
            }

            // set to extreme values to accept initial values from assignment
            int upperBound = int.MaxValue;
            int lowerBound = int.MinValue;
            int leftBound = int.MaxValue;
            int rightBound = int.MinValue;

            using (StreamReader sr = new StreamReader("layout.dat"))
            {
                y = _screenBounds.Top;
                for (int i = 0; i < _MaxLines; i++)
                {
                    string line = sr.ReadLine();
                    if (line == null)
                    {
                        break;
                    }

                    x = _screenBounds.Left;
                    for (int j = 0; j < _kBricksPerLine && j < line.Length; j++)
                    {
                        char c = line[j];
                        if (c != ' ')
                        {
                            if (!brickMap.Keys.Contains<char>(c))
                            {
                                c = 'R';
                            }
                            if (j < leftBound) leftBound = j;
                            if (j > rightBound) rightBound = j;
                            if (i < upperBound) upperBound = i;
                            if (i > lowerBound) lowerBound = i;

                            Brick.State[] states = brickMap[c];

                            Brick brick = new Brick(_game, states,
                                new Vector2(x + brickHalfSpaceWidth, y + brickHalfSpaceHeight),
                                this);

                            _game.Components.Add(brick);
                            this._bricks.Add(brick);
                        }

                        x += brickSpaceWidth;
                    }
                    y += brickSpaceHeight;
                }


            }
        }

        public void Collides(IPhysicalObject phyObj)
        {
            if (phyObj is Ball)
            {
                Ball ball = (Ball)phyObj;
                
                // TODO: pre-judgement
                // ...

                for (int i = 0; i < this._bricks.Count; )
                {
                    Brick brick = this._bricks[i];
                    if (brick.CollidesWithBall(ball))
                    {   // remove brick
                        for (int j = 0; j < _game.Components.Count; j++)
                        {
                            IGameComponent component = _game.Components[j];
                            if (component == brick)
                            {
                                _game.Components.RemoveAt(j);
                                break;
                            }
                        }
                        _bricks.RemoveAt(i);
                    }
                    else
                    {
                        i++;
                    }
                }
            }
        }
    }
}